package com.loong.web.gp4j.service.user;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.velocity.VelocityContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.loong.tool.lang.Strings;
import com.loong.tool.secret.security.digest.Digest;
import com.loong.web.gp4j.bean.param.user.RepwdParam;
import com.loong.web.gp4j.bean.param.user.UserPwdParam;
import com.loong.web.gp4j.bean.param.users.UserParam;
import com.loong.web.gp4j.bean.param.users.UserStatusParam;
import com.loong.web.gp4j.bean.param.users.UsersParam;
import com.loong.web.gp4j.bean.schema.code.Codes;
import com.loong.web.gp4j.bean.schema.user.Auths;
import com.loong.web.gp4j.bean.schema.user.UserAuthSchema;
import com.loong.web.gp4j.bean.schema.user.UserDetailSchema;
import com.loong.web.gp4j.bean.schema.user.UserSchema;
import com.loong.web.gp4j.bean.schema.user.UserStatuses;
import com.loong.web.gp4j.bean.vo.user.UserVo;
import com.loong.web.gp4j.component.mail.MailComponent;
import com.loong.web.gp4j.component.mail.MailSubjects;
import com.loong.web.gp4j.component.velocity.VelocityComponent;
import com.loong.web.gp4j.component.velocity.Vms;
import com.loong.web.gp4j.dao.user.UserAuthDao;
import com.loong.web.gp4j.dao.user.UserDao;
import com.loong.web.gp4j.dao.user.UserDetailDao;
import com.loong.web.gp4j.dao.user.UserTrans;
import com.loong.web.gp4j.service.code.CodeService;
import com.loong.web.suppert.auth.User;
import com.loong.web.suppert.auth.UserSessions;
import com.loong.web.suppert.collection.list.KvnList;
import com.loong.web.suppert.collection.list.KvnList.Kvn;
import com.loong.web.suppert.collection.param.PageParam;
import com.loong.web.suppert.collection.param.ParamMap;
import com.loong.web.suppert.collection.valid.ErrorList;
import com.loong.web.suppert.collection.vo.ResultVo;
import com.loong.web.suppert.collection.vo.ResultVoFactory;
import com.loong.web.suppert.collection.vo.RowsVo;

/**
 * 用户服务
 *
 * @author 张成轩
 */
@Service
public class UserService {

	@Resource
	private UserDao userDao;
	@Resource
	private UserDetailDao userDetailDao;
	@Resource
	private UserAuthDao userAuthDao;
	@Resource
	private CodeService codeService;
	@Resource
	private UserTrans userTrans;
	@Resource
	private MailComponent mailComponent;
	@Resource
	private VelocityComponent velocityComponent;

	@Resource(name = "user.statuses")
	private KvnList<String, Integer, String> statuses;
	@Resource(name = "code.auths")
	private KvnList<String, Integer, String> auths;

	@Resource(name = "order.type")
	private Map<String, String> orderType;
	@Resource(name = "order.field.user")
	private KvnList<String, String, String> orderField;

	@Value("${web.name}")
	private String webName;
	@Value("${web.url}")
	private String webUrl;

	/** 随机字符集 */
	private static final char[] CHARS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g', 'k', 'l', 'm', 'n', 'o',
			'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
	/** 随机密码长度 */
	private static final int PWD_LENGTH = 10;

	/**
	 * @return 状态集
	 */
	public KvnList<String, Integer, String> getStatuses() {

		return statuses;
	}

	/**
	 * @param request 请求
	 * @return 权限集
	 */
	public KvnList<String, Integer, String> getAuths(HttpServletRequest request) {

		if (UserSessions.has(request, Auths.SYS_MANAGER))
			return auths;
		// 系统管理员
		int sys = auths.getV(Auths.SYS_MANAGER);
		// 封装对象
		KvnList<String, Integer, String> as = new KvnList<String, Integer, String>();
		for (Kvn<String, Integer, String> a : auths) {
			if (a.getValue() == sys)
				continue;
			as.add(a);
		}
		return as;
	}

	/**
	 * @return 排序字段
	 */
	public KvnList<String, String, String> getOrder() {

		return orderField;
	}

	/**
	 * 修改密码
	 * 
	 * @param request 请求
	 * @param param 参数
	 * @param errorList 错误列表
	 * @return 是否成功
	 */
	public boolean editPassword(HttpServletRequest request, UserPwdParam param, ErrorList errorList) {

		User user = UserSessions.getUser(request);
		UserSchema userSchema = userDao.get(user.getId());
		if (userSchema == null) {
			errorList.addError("用户不存在");
			return false;
		}
		// 校验密码
		String password = Digest.digest(param.getPwd());
		if (!password.equals(userSchema.getPassword())) {
			errorList.addError("pwd", "密码错误");
			return false;
		}
		// 更新数据
		userSchema.setPassword(Digest.digest(param.getNpwd()));
		int count = userDao.update(userSchema);
		if (count != 1) {
			errorList.addError("更新失败");
			return false;
		}
		return true;
	}

	/**
	 * 查询用户列表
	 * 
	 * @param request 请求
	 * @param param 参数
	 * @param page 分页
	 * @return 返回对象
	 */
	public RowsVo<UserVo> queryUsers(HttpServletRequest request, UsersParam param, PageParam page) {

		ParamMap params = new ParamMap();
		if (Strings.isNotNull(param.getK()))
			params.putLike("k", param.getK());
		if (Strings.isNotNull(param.getStatuses()))
			params.put("statuses", param.getStatuses().split(","));
		if (Strings.isNotNull(param.getAuths()))
			params.put("auths", param.getAuths().split(","));
		// 查看用户是否有管理员权限
		User user = UserSessions.getUser(request);
		if (!user.has(Auths.SYS_MANAGER))
			params.put("ex_auth", auths.getV(Auths.SYS_MANAGER));
		// 不包含自己
		params.put("ex", user.getId());
		// 分页排序
		params.setPage(page);
		params.buildOrder(orderType, orderField);

		List<UserSchema> users = userDao.query(params);

		// 封装对象
		List<UserVo> userVos = new LinkedList<UserVo>();
		if (!users.isEmpty()) {
			// 查询代码集
			Map<Integer, String> sexs = codeService.getCodeMap(Codes.SEX);
			UserAdapter.convert(users, auths, statuses, sexs, userVos);
		}
		RowsVo<UserVo> vo = new RowsVo<UserVo>();
		vo.setPage(page);
		vo.setRows(userVos);
		return vo;
	}

	/**
	 * 修改用户状态
	 * 
	 * @param request 请求
	 * @param param 参数
	 * @return 返回对象
	 */
	public ResultVo<?> editStatus(HttpServletRequest request, UserStatusParam param) {

		Integer status = statuses.getV(param.getStatus());
		if (status == null)
			return ResultVoFactory.fail("用户状态不存在");
		// 获取用户信息
		UserSchema userSchema = userDao.get(param.getId());
		if (userSchema == null)
			return ResultVoFactory.fail("用户不存在");
		else if (userSchema.getStatus().equals(statuses.getV(UserStatuses.CANCEL)))
			return ResultVoFactory.fail("用户已销户，无法修改状态！");
		// 判断用户权限
		if (!hasAuth(request, userSchema.getId()))
			return ResultVoFactory.fail("无操作该用户权限");
		// 修改用户状态
		userSchema.setStatus(status);
		int count = userDao.update(userSchema);
		if (count != 1)
			return ResultVoFactory.fail("更新失败");
		return ResultVoFactory.succ();
	}

	/**
	 * 重置用户密码
	 * 
	 * @param request 请求
	 * @return 返回对象
	 */
	public ResultVo<?> rePassword(HttpServletRequest request) {

		// 重置自己的密码
		User u = UserSessions.getUser(request);
		UserSchema user = userDao.get(u.getId());
		if (Strings.isNull(user.getEmail()))
			return ResultVoFactory.fail("用户尚未登记邮箱，无法重置密码！");
		boolean succ = rePassword(user);
		if (!succ)
			return ResultVoFactory.fail("更新失败");
		return ResultVoFactory.succ();
	}

	/**
	 * 重置用户密码
	 * 
	 * @param request 请求
	 * @param id id
	 * @return 返回对象
	 */
	public ResultVo<?> rePassword(HttpServletRequest request, Integer id) {

		UserSchema user = userDao.get(id);
		if (user == null)
			return ResultVoFactory.fail("用户不存在");
		else if (user.getStatus().equals(statuses.getV(UserStatuses.CANCEL)))
			return ResultVoFactory.fail("用户已销户，无法重置密码！");
		else if (Strings.isNull(user.getEmail()))
			return ResultVoFactory.fail("用户尚未登记邮箱，无法重置密码！");
		// 判断用户权限
		if (!hasAuth(request, user.getId()))
			return ResultVoFactory.fail("无操作该用户权限");
		boolean succ = rePassword(user);
		if (!succ)
			return ResultVoFactory.fail("更新失败");
		return ResultVoFactory.succ();
	}

	/**
	 * 重置用户密码
	 * 
	 * @param request 请求
	 * @param param 参数
	 * @param errorList 错误列表
	 * @return 是否成功
	 */
	public boolean rePassword(RepwdParam param, ErrorList errorList) {

		UserSchema user = userDao.getByUser(param.getUser());
		if (user == null || user.getStatus().equals(statuses.getV(UserStatuses.CANCEL))
				|| !param.getEmail().equals(user.getEmail())) {
			errorList.addError("user", "用户或邮箱错误");
			return false;
		}
		return rePassword(user);
	}

	/**
	 * 重置用户密码
	 * 
	 * @param user 用户
	 * @return 是否成功
	 */
	private boolean rePassword(UserSchema user) {

		// 随机密码
		String pwd = random(PWD_LENGTH);
		user.setPassword(Digest.digest(pwd));
		int count = userDao.update(user);
		if (count != 1)
			return false;
		else
			// 发送邮件
			sendPasswordMail(user.getUser(), pwd, user.getEmail(), false);
		return true;
	}

	/**
	 * 获取用户信息
	 * 
	 * @param request 请求
	 * @param id id
	 * @return 用户对象
	 */
	public UserVo getUser(HttpServletRequest request, Integer id) {

		if (id == null)
			return null;
		UserSchema user = userDao.getWithDetail(id);
		if (user == null)
			return null;
		// 判断用户权限
		if (!hasAuth(request, user.getAuths()))
			return null;
		Map<Integer, String> sexs = codeService.getCodeMap(Codes.SEX);
		UserVo vo = new UserVo();
		UserAdapter.convert(user, auths, statuses, sexs, vo);
		return vo;
	}

	/**
	 * 添加用户
	 * 
	 * @param request 请求
	 * @param param 参数
	 * @return 是否成功
	 */
	public ResultVo<?> add(HttpServletRequest request, UserParam param) {

		if (param.getId() != null)
			return ResultVoFactory.fail("用户参数异常");
		UserSchema user = userDao.getByUser(param.getUser());
		if (user != null)
			return ResultVoFactory.fail("用户名已存在");
		// 随机密码
		String pwd = random(PWD_LENGTH);
		// 封装对象
		user = new UserSchema();
		user.setStatus(statuses.getV(UserStatuses.NORMAL));
		user.setPassword(Digest.digest(pwd));

		UserDetailSchema userDetail = new UserDetailSchema();
		List<UserAuthSchema> userAuths = new LinkedList<UserAuthSchema>();
		UserAdapter.convert(param, user);
		UserAdapter.convert(param, userDetail);
		UserAdapter.convert(param, userAuths);

		// 判断用户权限
		if (!hasAuth(request, userAuths))
			return ResultVoFactory.fail("用户赋权失败");

		// 插入数据
		int count = userTrans.insert(user, userDetail, userAuths);
		if (count != 1)
			return ResultVoFactory.fail("更新失败");
		else
			// 发送邮件
			sendPasswordMail(user.getUser(), pwd, user.getEmail(), true);
		return ResultVoFactory.succ();
	}

	/**
	 * 编辑用户
	 * 
	 * @param request 请求
	 * @param param 参数
	 * @return 是否成功
	 */
	public ResultVo<?> edit(HttpServletRequest request, UserParam param) {

		UserSchema user = userDao.get(param.getId());
		if (user == null)
			return ResultVoFactory.fail("用户不存在");
		else if (user.getStatus().equals(statuses.getV(UserStatuses.CANCEL)))
			return ResultVoFactory.fail("用户已销户");

		UserDetailSchema userDetail = userDetailDao.getByUserId(user.getId());
		if (userDetail == null)
			userDetail = new UserDetailSchema();

		List<UserAuthSchema> userAuths = new LinkedList<UserAuthSchema>();
		UserAdapter.convert(param, userDetail);
		UserAdapter.convert(param, userAuths);
		user.setEmail(param.getEmail());

		// 判断用户权限
		if (!hasAuth(request, userAuths) || !hasAuth(request, param.getId()))
			return ResultVoFactory.fail("用户赋权失败");

		// 插入数据
		int count = userTrans.update(user, userDetail, userAuths);
		if (count != 1)
			return ResultVoFactory.fail("更新失败");
		return ResultVoFactory.succ();
	}

	/**
	 * 发送密码邮件
	 * 
	 * @param user 用户名
	 * @param password 密码
	 * @param mail 邮箱地址
	 * @param newUser 新用户
	 */
	private void sendPasswordMail(String user, String password, String mail, boolean newUser) {

		VelocityContext vc = new VelocityContext();
		vc.put("user", user);
		vc.put("password", password);
		vc.put("url", webUrl);
		vc.put("web", webName);
		vc.put("new", newUser);
		String html = velocityComponent.context(Vms.MAIL_PASSWORD, vc);
		mailComponent.send(MailSubjects.PASSWORD, html, mail);
	}

	/**
	 * 随机字符串
	 * 
	 * @param length 长度
	 * @return 随机密码
	 */
	private String random(int length) {

		Random random = new Random();
		StringBuilder pwd = new StringBuilder();
		for (int i = 0; i < length; i++)
			pwd.append(CHARS[random.nextInt(CHARS.length)]);
		return pwd.toString();
	}

	/**
	 * 是否有权限
	 * 
	 * @param request 请求
	 * @param userId 待修改用户
	 * @return 是否有权限
	 */
	private boolean hasAuth(HttpServletRequest request, Integer userId) {

		// 判断用户权限
		if (!UserSessions.has(request, Auths.SYS_MANAGER)) {
			List<UserAuthSchema> userAuths = userAuthDao.getByUserId(userId);
			if (hasSysAuth(userAuths))
				return false;
		}
		return true;
	}

	/**
	 * 是否有权限
	 * 
	 * @param request 请求
	 * @param userAuths 待修改用户权限
	 * @return 是否有权限
	 */
	private boolean hasAuth(HttpServletRequest request, List<UserAuthSchema> userAuths) {

		// 判断用户权限
		if (!UserSessions.has(request, Auths.SYS_MANAGER))
			if (hasSysAuth(userAuths))
				return false;
		return true;
	}

	/**
	 * 是否包含系统管理员权限
	 * 
	 * @param userAuths 权限集
	 * @return 是否有
	 */
	private boolean hasSysAuth(List<UserAuthSchema> userAuths) {

		int sys = auths.getV(Auths.SYS_MANAGER);
		for (UserAuthSchema userAuth : userAuths)
			if (userAuth.getAuth() == sys)
				return true;
		return false;
	}
}
